﻿/*************************************************
  Copyright (C), 2014-2017, lmd Tech. Co., Ltd.
  文 件  名:	Assets\Scripts\Tools\UnityExtension\VectorUtil.cs
  作       者:	陈佳
  版       本:	1.0
  完成日期:	2017/07/13
  功能描述:		向量的工具类
  主要功能:  1,计算两个的角度偏移,(带方向)
				  2,检查某个向量是否在目标向量的误差范围内(多用于验证客户的服务器位置偏差)
				  3,计算某个向量在目标向量的左边还是右边
				  4	,让一个向量以一个角速度向目标向量旋转
*************************************************/
//#define DEBUG_VECTORUTIL

using UnityEngine;

namespace CxExtension
{
	public static class VectorUtil
	{
		/// <summary>
		/// 获取from到to的偏移夹角
		/// </summary>
		/// <param name="from"></param>
		/// <param name="to"></param>
		/// <returns>返回相对夹角,如果大于0 表示to在from右边,反之表示to在from的左边</returns>
		public static float AngleOffset(Vector3 from, Vector3 to)
		{
			var angle = Vector3.Angle(from, to);
			var isright = IsRight(from, to);
			angle = isright ? angle : -angle;
			return angle;
		}

		/// <summary>
		/// 检测某个坐标是否在目标值的允许误差范围内
		/// </summary>
		/// <param name="cur">待检测的值</param>
		/// <param name="dest">目标值,误差以目标值为中心半径为error</param>
		/// <param name="radius">误差半径</param>
		/// <returns>返回是否在允许范围内</returns>
		public static bool IsInRange(Vector3 cur, Vector3 dest, float radius = 0.001f)
		{
			//var dis = Vector3.Distance(cur,dest);
			var offset = dest - cur;
			bool result = offset.sqrMagnitude <= (radius * radius);
			return result;
		}

		/// <summary>
		/// 判断b向量在是否在a的右边
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static bool IsRight(Vector3 a, Vector3 b)
		{
			var off = RelativePos(a, b);
			return off > 0;
		}

		/// <summary>
		/// 判断b向量在a的左边右边还是同向
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns>小于零在左边,等于零在中间,大于0在右边</returns>
		public static float RelativePos(Vector3 a, Vector3 b)
		{
			var up = Vector3.Cross(a, b);
			var off = up.y;//Vector3.Dot(a,up);
			return off;
		}

		/// <summary>
		/// 以 angleVelocity 度/每秒  的角速度在dt时间内 使from 向to方向旋转	,并out旋转后的方向,
		/// </summary>
		/// <param name="from">当前方向</param>
		/// <param name="to">目标方向</param>
		/// <param name="angleVelocity">角速度 单位 度每秒</param>
		/// <param name="dt">使用的时间</param>
		/// <returns>返回值处于 from 和to之间 ,不过超出范围</returns>
		public static bool RotateByAngleVelocity(Vector3 from, Vector3 to, float angleVelocity, float dt, out Vector3 newDir)
		{
			from.Normalize();
			to.Normalize();
			var angle = Vector3.Angle(from, to);
			if(angle > 0.001f)
			{
				if(angleVelocity < 0)
				{
					Debuger.WrnFormat("RorateByAngleVelocity,转向速度不能为负数,{0}", angleVelocity);
					angleVelocity = Mathf.Abs(angleVelocity);
				}

				var s = angleVelocity * dt;
				var offset = Mathf.Min(angle, s);
				var percent = offset / angle;
				var newV = Vector3.Slerp(from, to, percent);
#if DEBUG_VECTORUTIL
				var newAngle = Vector3.Angle(newV,to);
				var faceRotate = angle - newAngle;
				Debug.LogFormat("from:{0},to:{1},velocity:{2},dt:{3},转前:{4},转后:{5},预期转:{6},实际转:{7}",from,to,angleVelocity,dt,angle,newAngle,s,faceRotate);
#endif
				newDir = newV;
				return true;
			}
			else
			{
				newDir = to;
				return false;
			}
		}

		public static Vector3 RotateByAngleVelocity(Vector3 from, Vector3 to, float angleVelocity, float dt)
		{
			Vector3 newDir;
			RotateByAngleVelocity(from, to, angleVelocity, dt, out newDir);
			return newDir;
		}

		/// <summary>
		/// 把一个方向围绕某个轴旋转多少度
		/// </summary>
		/// <param name="from"></param>
		/// <param name="angle"></param>
		/// <returns></returns>
		public static Vector3 RotateByAngle(Vector3 from, float angle)
		{
			return RotateByAngle(from, Vector3.up, angle);
		}

		/// <summary>
		/// 把一个方向围绕某个轴旋转多少度
		/// </summary>
		/// <param name="from"></param>
		/// <param name="axis"></param>
		/// <param name="angle"></param>
		/// <returns></returns>
		public static Vector3 RotateByAngle(Vector3 from, Vector3 axis, float angle)
		{
			var rotate = Quaternion.AngleAxis(angle, axis) * from;
			return rotate;
		}

		public static string ToString2d(this Vector3 self)
		{
			var str = string.Format("{0:f6},{1:f6}", self.x, self.z);
			return str;
		}
	}
}